package com.edfu.core.coursemgt.service.impl;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.edfu.core.commons.beans.EduObjectType;
import com.edfu.core.commons.service.api.INotificationService;
import com.edfu.core.commons.util.CategoryMgtBeanTransformer;
import com.edfu.core.coursemgt.persistence.api.ICourseMgtPersistence;
import com.edfu.core.coursemgt.persistence.api.ICourseOfferingMgtPersistence;
import com.edfu.core.coursemgt.persistence.entity.AcadSemCourseBranchRelnEntity;
import com.edfu.core.coursemgt.persistence.entity.AcadSemCourseBranchUserRelnEntity;
import com.edfu.core.coursemgt.persistence.entity.CourseCourseOfferingRelnEntity;
import com.edfu.core.coursemgt.persistence.entity.CourseEntity;
import com.edfu.core.coursemgt.persistence.entity.CourseOfferingEntity;
import com.edfu.core.coursemgt.persistence.entity.CourseOfferingInstanceEntity;
import com.edfu.core.coursemgt.persistence.entity.CourseOfferingOwnerRelnEntity;
import com.edfu.core.coursemgt.persistence.entity.LessonEntity;
import com.edfu.core.coursemgt.persistence.entity.LessonInstanceEntity;
import com.edfu.core.coursemgt.persistence.entity.LessonUnitEntity;
import com.edfu.core.coursemgt.service.api.ICourseMgtService;
import com.edfu.core.coursemgt.util.ContentMgtHook;
import com.edfu.core.coursemgt.util.CourseMgtConstants;
import com.edfu.core.coursemgt.util.CourseMgtModelConverter;
import com.edfu.core.coursemgt.vo.Course;
import com.edfu.core.coursemgt.vo.CourseOffering;
import com.edfu.core.coursemgt.vo.CourseOfferingInstance;
import com.edfu.core.coursemgt.vo.Enrollment;
import com.edfu.core.coursemgt.vo.Lesson;
import com.edfu.core.coursemgt.vo.LessonUnit;
import com.edfu.core.identitymgt.persistence.api.IIdentityMgtPersistence;
import com.edfu.core.identitymgt.persistence.entity.UserEnrollmentEntity;
import com.edfu.core.identitymgt.persistence.entity.UserEntity;
import com.edfu.core.institutemgt.persistence.api.IInstituteMgtPersistence;
import com.edfu.core.institutemgt.persistence.entity.AcadSemBranchRelnEntity;
import com.edfu.core.institutemgt.persistence.entity.AcadSemEntity;
import com.edfu.core.institutemgt.persistence.entity.BatchEntity;
import com.edfu.core.institutemgt.persistence.entity.BranchMemberEntity;
import com.edfu.core.util.Response;

public class CourseMgtServiceImpl implements ICourseMgtService {
	
	@Autowired
	private ICourseMgtPersistence courseMgtPersistence;
	
	@Autowired
	private ICourseOfferingMgtPersistence courseOfferingMgtPersistence;

	@Autowired
	private IIdentityMgtPersistence identityMgtPersistence;
	
	@Autowired
	private IInstituteMgtPersistence instituteMgtPersistence;
	
	@Autowired
	private INotificationService notificationService;	

//	@Autowired
	private ContentMgtHook contentMgtHook;
	
	
	public ICourseMgtPersistence getCourseMgtPersistence() {
		return courseMgtPersistence;
	}

	public void setCourseMgtPersistence(ICourseMgtPersistence courseMgtPersistence) {
		this.courseMgtPersistence = courseMgtPersistence;
	}

	public IIdentityMgtPersistence getIdentityMgtPersistence() {
		return identityMgtPersistence;
	}

	public void setIdentityMgtPersistence(
			IIdentityMgtPersistence identityMgtPersistence) {
		this.identityMgtPersistence = identityMgtPersistence;
	}

	public IInstituteMgtPersistence getInstituteMgtPersistence() {
		return instituteMgtPersistence;
	}

	public void setInstituteMgtPersistence(
			IInstituteMgtPersistence instituteMgtPersistence) {
		this.instituteMgtPersistence = instituteMgtPersistence;
	}

	public ContentMgtHook getContentMgtHook() {
		return contentMgtHook;
	}

	public void setContentMgtHook(ContentMgtHook contentMgtHook) {
		this.contentMgtHook = contentMgtHook;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Response addCourse(Course course) {
		CourseEntity ce = CourseMgtModelConverter.getCourseEntity(course);
		ce = courseMgtPersistence.merge(ce);
		notificationService.addInterestsNotification(ce.getId(), EduObjectType.COURSE, CategoryMgtBeanTransformer.transformCategory(ce.getCategory()));
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	
	@Override
	public Response updateCourse(Course course) {
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	
	@Override
	public Response deleteCourse(String courseCode) {
		CourseEntity entity = courseMgtPersistence.getCourse(courseCode);
		courseMgtPersistence.remove(entity);
		notificationService.deleteInterestsNotification(entity.getId(), EduObjectType.COURSE);
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Response addCourseInSemesterForBranch(String courseCode,
			String branchCode, String semesterCode) {
		AcadSemCourseBranchRelnEntity ascbre = new AcadSemCourseBranchRelnEntity();
		AcadSemBranchRelnEntity asbre = courseMgtPersistence.getAcadSemBranchReln(branchCode, semesterCode);
		CourseEntity ce = courseMgtPersistence.getCourse(courseCode);		
		ascbre.setSemInBranch(asbre);
		ascbre.setCourse(ce);
		courseMgtPersistence.persist(ascbre);
		
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	
	@Override
	public Response removeCourseFromSemesterForBranch(String courseCode,
			String semesterCode, String branchCode) {
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	
	@Override
	public Response removeMember(String batchMemberId, String batchCode) {
		Response r = new Response();
		r.setSuccessful(true);
		return r;
		
	}
	@Override
	public Response enrollUserToMeeting(Enrollment enrollment) {
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	
	@Override
	public Response removeUserFromMeeting(String userId, String meetingCode) {
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	

	
	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Response associateCourseOfferingToCourse(String courseOfferingCode,
			String courseCode) {
		CourseOfferingEntity coe = courseOfferingMgtPersistence.getCourseOffering(courseOfferingCode) ;
		CourseEntity ce = courseMgtPersistence.getCourse(courseCode);
		CourseCourseOfferingRelnEntity ccoe = new CourseCourseOfferingRelnEntity();
		ccoe.setCourse(ce);
		ccoe.setCourseOffering(coe);
		courseMgtPersistence.persist(ccoe);
		
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	

	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Response teachCourseOffering(CourseOfferingInstance courseOfferingInstance) {
		if ( courseOfferingInstance.getOwnerUserId() == null || courseOfferingInstance.getOwnerUserId().trim().length() == 0)
			throw new RuntimeException("No owner specified for instance");
		
		UserEntity instanceOwner = identityMgtPersistence.getUser(courseOfferingInstance.getOwnerUserId());
		CourseOfferingEntity coe = courseOfferingMgtPersistence.getCourseOffering(courseOfferingInstance.getCourseOfferingCode());
		BatchEntity be = instituteMgtPersistence.getBatch(courseOfferingInstance.getBatchCode());
		AcadSemEntity ase = instituteMgtPersistence.getSemester(courseOfferingInstance.getSemsesterCode());
		List<AcadSemCourseBranchRelnEntity> coursesBranchSemList = new ArrayList<AcadSemCourseBranchRelnEntity>();
		//List<AcadSemCourseBranchRelnEntity> coursesBranchSemList = courseMgtPersistence.getCoursesToBeTaughtInGivenSem(be.getBranch().getCode(), courseOfferingInstance.getSemsesterCode());
		List<CourseCourseOfferingRelnEntity> courseCourseOfferingList = courseMgtPersistence.getAssociatedCoursesForOffering(courseOfferingInstance.getCourseOfferingCode());
		
		boolean validCourseOffering = false ; 
		
		for ( CourseCourseOfferingRelnEntity courseCourseOffering : courseCourseOfferingList ) {
			String courseCode = courseCourseOffering.getCourse().getCode();
			for (AcadSemCourseBranchRelnEntity courseBranchSem : coursesBranchSemList ) {
				if ( courseBranchSem.getCourse().getCode().equals(courseCode)) {
					validCourseOffering = true ;
					break;
				}
			}
			if ( validCourseOffering )
				break;
		}
		
		if ( !validCourseOffering ) {
			throw new RuntimeException("Cannot assign courseoffering to batch");
		}
		
		CourseOfferingInstanceEntity coie = CourseMgtModelConverter.getCourseOfferingInstanceEntity(courseOfferingInstance);
		coie.setBatch(be);
		coie.setCourseOffering(coe);
		//coie.setSemester(ase);
		coie.setOwner(instanceOwner);
		
		courseMgtPersistence.persist(coie);
		
		if ( coe.getLessons() != null ) {
			for (LessonEntity le : coe.getLessons() ) {
				LessonInstanceEntity lessonInstEntity = new LessonInstanceEntity();
				lessonInstEntity.setCourseOfferingInstance(coie);
				lessonInstEntity.setLesson(le);
				lessonInstEntity.setStatus("ACTIVE");
				lessonInstEntity.setVisible(true);
				courseMgtPersistence.persist(lessonInstEntity);
			}
		}
		
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	

	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Response assignTeacherToBranch(String userId, String branchCode) {
		List<UserEnrollmentEntity> enrollmentsList = courseMgtPersistence.getUserEnrollments(userId, "TEACHER");
		
		//Check if user has teacher role assigned to him
		boolean hasTeacherRole = false ;
		for ( UserEnrollmentEntity enrollment : enrollmentsList ) {
			if ( enrollment.getRole().getName().equalsIgnoreCase("TEACHER") ) {
				hasTeacherRole = true ;
				break;
			}
		}
		if (!hasTeacherRole) {
			throw new RuntimeException("User does not have teacher role assigned");
		}
		
		//Ensure that user has not already been assigned as teacher in given branch
		if (instituteMgtPersistence.getBranchMember(userId, "TEACHER", branchCode) != null ) {
			throw new RuntimeException("User already a teacher for the branch");
		}else {
			BranchMemberEntity bme = new BranchMemberEntity();
			bme.setAddedOn(Calendar.getInstance().getTime());
			bme.setBranch(instituteMgtPersistence.getBranch(branchCode));
			bme.setMember(identityMgtPersistence.getUser(userId));
			bme.setRole(identityMgtPersistence.getRole("TEACHER"));
			instituteMgtPersistence.persist(bme);
		}
		
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	
	@Override
	public Response assignTeacherToCourse(String userId, String courseCode,
			String branchCode) {
		return null;
	}

	@Override
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { RuntimeException.class })
	public Response assignTeacherToCourse(String userId, String courseCode,
			String semesterCode, String branchCode) {
		List<UserEnrollmentEntity> enrollmentsList = courseMgtPersistence.getUserEnrollments(userId, "TEACHER");
		
		//Check if user has teacher role assigned to him
		boolean hasTeacherRole = false ;
		if ( enrollmentsList != null && enrollmentsList.size() > 0)
			hasTeacherRole = true;
		
		if (!hasTeacherRole) {
			throw new RuntimeException("User does not have teacher role assigned");
		}
		
		//Do nothing if teacher already assigned to course
		if (courseMgtPersistence.teaches(userId, courseCode, branchCode, semesterCode))
			throw new RuntimeException("Already assigned as teacher for the course");
			
		AcadSemCourseBranchRelnEntity ascbe = courseMgtPersistence.getCourseIfTaughtInSem(courseCode, branchCode, semesterCode);
		AcadSemCourseBranchUserRelnEntity e = new AcadSemCourseBranchUserRelnEntity();
		e.setCourseInGivenSemInBranch(ascbe);
		e.setUserEnrollment(enrollmentsList.get(0));
		
		courseMgtPersistence.persist(e);
		
		Response r = new Response();
		r.setSuccessful(true);
		return r;
	}
	

	@Override
	@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = { RuntimeException.class })
	public List<CourseOfferingInstance> getCourseOfferingInstancesCurrentlyTaughtByMe(
			String userId) {
		List<CourseOfferingInstance> myCourseOfferings = null;
		List<CourseOfferingInstanceEntity> list = courseMgtPersistence.getCourseOfferingInstancesCurrentlyTaughtByMe(userId);
		if (list != null && list.size() > 0) {
			myCourseOfferings = new ArrayList<CourseOfferingInstance>();
			for (CourseOfferingInstanceEntity coie : list) {
				myCourseOfferings.add(CourseMgtModelConverter.getCourseOfferingInstance(coie));
			}
		}
		return myCourseOfferings;
	}
	
	@Override
	public List<CourseOfferingInstance> getAllCourseOfferingInstancesAuthoredByMe(
			String userId) {
		// TODO Auto-generated method stub
		return null;
	}
	
	
	
	@Override
	@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = { RuntimeException.class })
	public List<CourseOfferingInstance> getCourseOfferingInstancesCurrentlyTaughtToMe(
			String userId) {
		List<CourseOfferingInstance> courseOfferingsForMe = null;
		List<CourseOfferingInstanceEntity> list = courseMgtPersistence.getCourseOfferingInstancesCurrentlyTaughtToMe(userId);
		if(list != null && list.size() > 0) {
			courseOfferingsForMe = new ArrayList<CourseOfferingInstance>();
			for (CourseOfferingInstanceEntity coie : list) {
				courseOfferingsForMe.add(CourseMgtModelConverter.getCourseOfferingInstance(coie));
			}
		}
		return courseOfferingsForMe;
	}
	

}
